package cn.sunxinao.pm.services;

import cn.sunxinao.pm.models.AbstractUser;
import cn.sunxinao.pm.utils.Common;
import cn.sunxinao.pm.utils.IReadWriteTool;
import java.io.File;
import java.io.IOException;
import java.util.List;
import java.util.Scanner;

/**
 * 用户管理抽象类
 */
public abstract class AbstractUserManager {
    protected final List<AbstractUser> users;
    protected final IReadWriteTool readWriteTool;
    protected final File savePath;

    public AbstractUserManager(IReadWriteTool readWriteTool, File savePath) {
        this.savePath = savePath;
        if (!this.savePath.exists()) { // 创建文件
            try {
                if (!savePath.createNewFile())
                    System.err.println("提示: 无法创建文件，数据可能无法保存");
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        this.readWriteTool = readWriteTool;
        this.users = readWriteTool.readAllUsers(savePath);
    }

    /**
     * 根据用户名和密码创建用户对象
     * （由子类实现）
     *
     * @param uName 用户名
     * @param uPass 密码的 md5
     * @return 用户对象
     */
    public abstract AbstractUser createUser(String uName, String uPass);

    /**
     * 登陆账号
     *
     * @param kb 以标准输入实例化的 Scanner
     * @return 是否登陆成功
     */
    public boolean login(Scanner kb) {
        System.out.println("请输入管理用户名(回车进行注册):");
        String uName = kb.nextLine().trim();
        if (uName.isEmpty()) {
            try {
                uName = register(kb);
            } catch (Exception e) {
                System.err.println(e.getMessage());
                return false;
            }
        }
        return auth(uName, kb) != null;
    }

    /**
     * 判断用户名是否存在
     *
     * @param username 用户名
     * @return 是否存在
     */
    public boolean usernameExist(String username) {
        for (AbstractUser user : users) {
            if (username.equals(user.getUsername())) return true;
        }
        return false;
    }

    /**
     * 注册新的管理账号
     *
     * @param kb 以标准输入实例化的Scanner
     * @return 如果注册成功，则为新用户的用户名
     * @throws Exception 包含无法注册的原因
     */
    public String register(Scanner kb) throws Exception {
        System.out.println("请输入用户名");
        String uName;
        do {
            uName = kb.nextLine().trim();
        } while (uName.isEmpty());

        while (usernameExist(uName)) {
            System.err.println("用户[" + uName + "]已存在，请重试");
            do {
                uName = kb.nextLine().trim();
            } while (uName.isEmpty());
        }
        System.out.println("请输入密码:");
        String uPass;
        do {
            uPass = Common.md5Hex(kb.nextLine());
        } while (uPass.isEmpty());
        System.out.println("请再次输入密码:");
        if (!uPass.equals(Common.md5Hex(kb.nextLine()))) throw new Exception("密码不一致，你确定记住它了吗？");
        if (!checkVerifyCode(kb)) throw new Exception("连续错误3次，取消操作");
        AbstractUser user = createUser(uName, uPass);
        users.add(user);
        System.out.println("注册成功，请重新登陆\n");
        return uName;
    }

    /**
     * 根据用户名查找用户对象
     *
     * @param uName 用户名
     * @return 用户对象，不存在时为 null
     */
    private AbstractUser findUser(String uName) {
        for (AbstractUser user : users) {
            if (user.getUsername().equals(uName)) return user;
        }
        return null;
    }

    /**
     * 要求用户认证
     *
     * @param uName 用户名
     * @param kb    Scanner
     * @return 认证成功的用户，认证失败为 null
     */
    private AbstractUser auth(String uName, Scanner kb) {
        if (uName.isEmpty()) return null;
        AbstractUser user = findUser(uName);
        if (user == null) {
            System.err.println("用户不存在");
            return null;
        }
        System.out.println("请输入密码:");
        String uPass = Common.md5Hex(kb.nextLine());
        if (uPass.isEmpty()) {
            System.err.println("密码不能为空");
            return null;
        }
        if (!user.getPassword().equals(uPass)) {
            System.err.println("密码错误");
            return null;
        }
        return user;
    }

    /**
     * 要求输入验证码
     *
     * @param kb Scanner
     * @return 是否验证成功
     */
    private boolean checkVerifyCode(Scanner kb) {
        String code = Common.randString(6);
        System.out.println("请输入验证码[" + code + "]:");
        for (int i = 0; !code.equalsIgnoreCase(kb.nextLine().trim()); i++) {
            System.err.println("验证码错误，请重试[" + (code = Common.randString(6)) + "]:");
            if (i > 2) {
                System.err.println("连续错误3次，取消操作");
                return false;
            }
        }
        return true;
    }

    /**
     * 删除账号
     *
     * @param uName 用户名
     * @param kb    以标准输入实例化的Scanner
     * @return 是否成功
     */
    public boolean unRegister(String uName, Scanner kb) {
        AbstractUser user = auth(uName, kb);
        if (user == null) return false;
        if (!checkVerifyCode(kb)) return false;
        users.remove(user);
        return true;
    }

    /**
     * 更改密码
     *
     * @param uName 用户名
     * @param kb    以标准输入实例化的Scanner
     * @return 是否成功
     */
    public boolean changePassword(String uName, Scanner kb) {
        AbstractUser user = auth(uName, kb);
        if (user == null) return false;
        System.out.println("请输入新的密码:");
        String uPass = Common.md5Hex(kb.nextLine());
        if (uPass.isEmpty()) {
            System.err.println("密码不能为空");
            return false;
        }
        System.out.println("请再次输入密码:");
        if (!uPass.equals(Common.md5Hex(kb.nextLine()))) {
            System.err.println("密码不一致，你确定记住它了吗？");
            return false;
        }
        if (!checkVerifyCode(kb)) return false;
        user.setPassword(uPass);
        return true;
    }

    /**
     * 保存
     *
     * @return 是否成功
     */
    public boolean save() {
        return readWriteTool.saveAllUsers(users, savePath);
    }

}
